package br.ufrj.dcc.ad.event;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import br.ufrj.dcc.ad.log.LoggerFactory;
import br.ufrj.dcc.ad.systems.Cliente;
import br.ufrj.dcc.ad.systems.Fila;
import br.ufrj.dcc.ad.systems.Servidor;

public class CancelScheduledAtendmentsEvent extends Event {
	private List<Event> listaEventos;
	private ServerAtendmentEvent eventoGerador;
	private Logger debugLogger = LoggerFactory.getLogger(LoggerFactory.Type.DEBUG);
	
	/**
	 * Cancela os eventos ServerAtendmentEvent com clientes menos prioritários.
	 * Garante que somente haverá um ServerAtendmentEvent por unidade de tempo.
	 * @param gerador O cliente que pediu o cancelamento dos agendamentos.
	 * @param eventoGerador O evento de consumo que solicitou o cancelamento dos eventos de consumo menos priritários.
	 * @param servidor O servidor que sofrerá o evento.
	 * @param tempoOcorrencia O tempo de ocorrência deste evento.
	 */
	public CancelScheduledAtendmentsEvent(Cliente gerador,ServerAtendmentEvent eventoGerador, Servidor servidor, double tempoOcorrencia) {
		super(gerador,servidor,tempoOcorrencia);
		this.eventoGerador = eventoGerador;
	}

	@Override
	public List<Event> executaAcao() {
		
		List<Event> eventosIvocacao = new ArrayList<Event>();
		List<Event> eventosIterados = new ArrayList<Event>(listaEventos);
		//Retirar todos os eventos de consumo que possuem prioridade mais baixa ou igual a deste cliente(gerador).
		for(Event evento: eventosIterados) {
			if(evento instanceof ServerAtendmentEvent){
				//Se o cliente gerador do evento de consumo gerador deste evento for mais prioritário que 
				//o gerador do evento serverAtendmentEvent iterado.
				if(gerador.comparePrioridade(evento.getGerador()) > 0){
					removeAtendmentEvent((ServerAtendmentEvent)evento);
				//... Se houver empate decidor usando a política da fila.	
				}else if((gerador.comparePrioridade(evento.getGerador()) == 0)){
					if(!eventoGerador.equals((ServerAtendmentEvent)evento)) {
						//Decidir quem remover usando a política da fila.
						decidirEmpate(eventoGerador,(ServerAtendmentEvent)evento);
					}
				//Se houver eventos com maior prioridade o evento deste cliente deve ser retirado(gerador).
				}else{
					removeAtendmentEvent(eventoGerador);
				}
			}
		}
		return eventosIvocacao;
	}

	private void decidirEmpate(ServerAtendmentEvent eventoGerador, ServerAtendmentEvent evento) {
		
		Fila fila = servidor.getFilasDePrioridade().get(eventoGerador.getGerador().getTipoCliente());
		Cliente clienteMenosPrioritario = fila.lessPriority(eventoGerador.getGerador(),evento.getGerador());
		if(eventoGerador.getGerador().equals(clienteMenosPrioritario)){
			removeAtendmentEvent(eventoGerador);
		}else{
			removeAtendmentEvent(evento);
		}
		
	}

	private void removeAtendmentEvent(ServerAtendmentEvent evento) {
		debugLogger.info(evento.toDebugString("Evento Excluido: ","EXCLUIDO"));
		listaEventos.remove(evento);
		removeCancelEventsAssociados(evento);
		realocarClienteNaFila(evento.getGerador());
	}

	private void removeCancelEventsAssociados(ServerAtendmentEvent serverAtendmentEvent) {
		List<Event> eventosIterados = new ArrayList<Event>(listaEventos);
		for(Event evento:eventosIterados){
			if(evento instanceof CancelScheduledAtendmentsEvent){
				if(evento.getGerador().equals(serverAtendmentEvent.getGerador())){
					debugLogger.info(evento.toDebugString("Evento Excluido: ","EXCLUIDO"));
					listaEventos.remove(evento);
				}
			}
		}
	}

	private void realocarClienteNaFila(Cliente cliente) {
		Fila fila = servidor.getFilasDePrioridade().get(cliente.getTipoCliente());
		fila.reenter(cliente);
	}

	public List<Event> getListaEventos() {
		return listaEventos;
	}

	public void setListaEventos(List<Event> listaEventos) {
		this.listaEventos = listaEventos;
	}

}
